#include "common.h"
#include "memory/cache.h"
#include "stdlib.h" //包含使用随机数种子所需函数
#include "burst.h"

//重复声明一下，防止显示未定义
uint32_t dram_read(hwaddr_t addr, size_t len);
void dram_write(hwaddr_t addr, size_t len, uint32_t data);
//内存读，存到cache里
void call_ddr3_read(hwaddr_t addr,void* data);
//从cache写到内存里
void call_ddr3_write(hwaddr_t addr,void* data,uint8_t* mask);


void init_cache(){
    //随机数种子，用于后面的随机替换策略，放在这个位置是为了只调用一次，保证随机数的平均
    srand(0);
    int i;
    for(i = 0;i < cache1_S * cache1_E;i++){
        cache1[i].valid = false;
        cache1[i].tag = 0;
        memset(cache1[i].cache_line,0,cache_B);
    }
    for(i = 0;i < cache2_S * cache2_E;i++){
        cache2[i].valid = false;
        cache2[i].tag = 0;
        memset(cache2[i].cache_line,0,cache_B);
    }
}

uint32_t cache1_read(hwaddr_t addr){
    uint32_t set = (addr >> cache_b) & ((1 << cache1_s) - 1);
    uint32_t tag = addr >> (cache1_s + cache_b);
    bool hit = false;

    int i;
    for(i = set * cache1_E;i < (set + 1) * cache1_E;i++){
        if(cache1[i].valid && cache1[i].tag == tag){
            hit = true;
            break;
        }
    }
    if(!hit){//未命中
        //找空的
        for(i = set * cache1_E;i < (set + 1) * cache1_E;i++){
            if(!cache1[i].valid) break; 
        }
        //没有空的，随机替换策略
        if(i == (set + 1) * cache1_E) i = set * cache1_E + rand() % cache1_E;
        cache1[i].valid = true;
        cache1[i].tag = tag;
        //下面从cache_L2中复制整行过来
        int j = cache2_read(addr);
        memcpy(cache1[i].cache_line,cache2[j].cache_line,cache_B);
    }
    return i;
}


void cache1_write(hwaddr_t addr,size_t len,uint32_t data){
    uint32_t set = (addr >> cache_b) & ((1 << cache1_s) - 1);
    uint32_t offset = addr & ((1 << cache_b) - 1);
    uint32_t tag = addr >> (cache1_s+cache_b);
    bool hit = false;

    int i;
    for(i = set * cache1_E;i < (set + 1) * cache1_E;i++){
        if(cache1[i].valid && cache1[i].tag == tag){
            hit = true;
            break;
        }
    }
    if(hit){//直写
        //先写到cache_L1里
        memcpy(cache1[i].cache_line + offset,&data,len); 
    }
    //写命中：直写：先写到cache_L1里，再写到cache_l2里
    //写未命中：非写分配:直接写到cache_L2里
    cache2_write(addr,len,data);
}




uint32_t cache2_read(hwaddr_t addr){
    uint32_t set = (addr >> cache_b) & ((1 << cache2_s) - 1);
    uint32_t tag = addr >> (cache2_s + cache_b);
    hwaddr_t block_start = (addr >> cache_b) << cache_b;
    bool hit = false;

    int i,j;
    for(i = set * cache2_E;i < (set + 1) * cache2_E;i++){
        if(cache2[i].valid && cache2[i].tag == tag){
            hit = true;
            break;
        }
    }
    if(!hit){//未命中
        //找空的
        for(i = set * cache2_E;i < (set + 1) * cache2_E;i++){
            if(!cache2[i].valid) break; 
        }
        //没有空的，随机替换策略
        if(i == (set + 1) * cache2_E){
            i = set * cache2_E + rand() % cache2_E;
            //写回
            if (cache2[i].dirty){
                uint8_t mask[BURST_LEN * 2];
                memset(mask, 1, BURST_LEN * 2);
                for (j = 0; j < cache_B / BURST_LEN; j++){
                    call_ddr3_write(block_start + j * BURST_LEN, cache2[i].cache_line + j * BURST_LEN, mask);
                }
            }
        }
        cache2[i].valid = true;
        cache2[i].tag = tag;
        cache2[i].dirty = false;
        //下面从内存中复制整行过来
        for(j = 0;j * BURST_LEN < cache_B;j++){ //ddr3每次只读写BURST_LEN长度数据
            call_ddr3_read(block_start + j * BURST_LEN,cache2[i].cache_line + j * BURST_LEN);
        }
    }
    return i;
}


void cache2_write(hwaddr_t addr,size_t len,uint32_t data){
    uint32_t set = (addr >> cache_b) & ((1 << cache2_s) - 1);
    uint32_t offset = addr & ((1 << cache_b) - 1);
    uint32_t tag = addr >> (cache2_s+cache_b);
    bool hit = false;

    int i;
    for(i = set * cache2_E;i < (set + 1) * cache2_E;i++){
        if(cache2[i].valid && cache2[i].tag == tag){
            hit = true;
            break;
        }
    }
    if(!hit){//写分配
        //把内存复制过来，再去更新这一行
        i = cache2_read(addr);
    }
    cache2[i].dirty = true;
    //写数据
    memcpy(cache2[i].cache_line + offset,&data,len);
}
